use super::entity;
use nom::{
    bytes::complete::{is_a, tag},
    multi::many0,
    IResult,
};

pub const LEVEL_SAPCE_LEN: usize = 2;
pub fn level_space(level: usize) -> String {
    String::from(" ".repeat(level * LEVEL_SAPCE_LEN))
}

#[derive(Debug, PartialEq, Clone)]
pub(crate) enum TagType {
    GroupData,
    Item,
}

#[derive(Debug, PartialEq)]
pub(crate) struct Tag {
    typ: TagType,
    level: usize,
}

impl Tag {
    pub(crate) fn parse(input: &str) -> IResult<&str, Tag> {
        let (input, space_len) = Self::space_len(input)?;
        let (input, tag_type) = Self::tag_type(input)?;
        let level = space_len / LEVEL_SAPCE_LEN;
        Ok((
            input,
            Tag {
                typ: tag_type,
                level,
            },
        ))
    }

    fn space_len(input: &str) -> IResult<&str, usize> {
        let (input, spaces) = many0(tag(" "))(input)?;
        Ok((input, spaces.len()))
    }

    fn tag_type(input: &str) -> IResult<&str, TagType> {
        let (input, tag_str) = is_a(
            ([entity::GROUP_TAG, entity::ITEM_TAG])
                .iter()
                .collect::<String>()
                .as_str(),
        )(input)?;
        let tag_str_char: char = tag_str.chars().next().unwrap_or_else(|| ' ');
        let tag: TagType = match tag_str_char {
            entity::GROUP_TAG => TagType::GroupData,
            _ => TagType::Item,
        };
        Ok((input, tag))
    }

    pub(crate) fn level(&self) -> usize {
        self.level
    }

    pub(crate) fn typ(&self) -> &TagType {
        &self.typ
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use nom::error::{Error, ErrorKind};
    use nom::Err;
    #[test]
    fn parse_works() {
        let tag = Tag::parse("  g[dsafd]");
        assert_eq!(
            Ok((
                "[dsafd]",
                Tag {
                    typ: TagType::GroupData,
                    level: 1,
                }
            )),
            tag
        );
        let tag = Tag::parse("   g[dsafd]");
        assert_eq!(
            Ok((
                "[dsafd]",
                Tag {
                    typ: TagType::GroupData,
                    level: 1,
                }
            )),
            tag
        );
        let tag = Tag::parse("    g[dsafd]");
        assert_eq!(
            Ok((
                "[dsafd]",
                Tag {
                    typ: TagType::GroupData,
                    level: 2,
                }
            )),
            tag
        );
        let tag = Tag::parse("g[dsafd]");
        assert_eq!(
            Ok((
                "[dsafd]",
                Tag {
                    typ: TagType::GroupData,
                    level: 0,
                }
            )),
            tag
        );
    }

    #[test]
    fn space_len_works() {
        let res = Tag::space_len("  g[dsafd]");
        assert_eq!(Ok(("g[dsafd]", 2)), res);
        let res = Tag::space_len("   g[dsafd]");
        assert_eq!(Ok(("g[dsafd]", 3)), res);
        let res = Tag::space_len("g[dsafd]");
        assert_eq!(Ok(("g[dsafd]", 0)), res);
        let res = Tag::space_len(" g[dsafd]");
        assert_eq!(Ok(("g[dsafd]", 1)), res);
    }

    #[test]
    fn tag_type_works() {
        let res = Tag::tag_type("g[dsafd]");
        assert_eq!(Ok(("[dsafd]", TagType::GroupData)), res);
        let res = Tag::tag_type("i[dsafd]");
        assert_eq!(Ok(("[dsafd]", TagType::Item)), res);
        let res = Tag::tag_type("r[dsafd]");
        assert_eq!(res, Err(Err::Error(Error::new("r[dsafd]", ErrorKind::IsA))))
    }
}
